package com.kelvem.saas.workbanch.fsm.resolver;

import com.kelvem.saas.workbanch.fsm.anno.Fsm;
import com.kelvem.saas.workbanch.fsm.anno.event.RegisterEvent;
import com.kelvem.saas.workbanch.fsm.anno.state.EventState;
import com.kelvem.saas.workbanch.fsm.anno.state.EventStateGroup;
import com.kelvem.saas.workbanch.fsm.container.FsmContainer;
import com.kelvem.saas.workbanch.fsm.entity.Entity;
import com.kelvem.saas.workbanch.fsm.entity.FsmEntity;
import com.kelvem.saas.workbanch.fsm.model.EventNode;
import com.kelvem.saas.workbanch.fsm.model.FsmNode;
import com.kelvem.saas.workbanch.fsm.model.StateGroupNode;
import com.kelvem.saas.workbanch.fsm.model.StateNode;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class FsmResolver {

    public static void resolve(Object bean, String beanName) {
        Class<?> eventClass = bean.getClass().getSuperclass();

        /**
         * fsm
         */
        Fsm fsmAnno = eventClass.getAnnotation(Fsm.class);

        // todo kelvem 注解类型转化
        Class<FsmEntity> entityClazz = (Class<FsmEntity>)fsmAnno.entityClazz();
        String entityName = entityClazz.getSimpleName();
        Class<FsmEntity> parentEntityClazz = (Class<FsmEntity>)fsmAnno.parentEntityClazz();
        if (Entity.class.equals(parentEntityClazz)) {
            parentEntityClazz = null;
        }

        /**
         * state
         */
        List<StateNode> stateList = getStateList(eventClass, entityName);
        List<StateGroupNode> groupList = getStateGroupList(eventClass, entityName);

        /**
         * event
         */
        List<EventNode> eventList = getEventList(eventClass, entityClazz, bean, beanName);

        /**
         * 组合
         */
        FsmNode fsm = FsmContainer.addData(entityClazz, parentEntityClazz, stateList, groupList, eventList);
        if (log.isDebugEnabled()) {
            log.debug("收集@Fsm关联注解信息, " + fsm);
        }
    }
    private static List<StateNode> getStateList(Class<?> eventClass, String entityName) {
        List<StateNode> stateList = new ArrayList<>();
        Annotation[] annotations = eventClass.getAnnotations();
        for (Annotation anno : annotations) {
            if (anno instanceof EventState) {
                EventState eventStateAnno = (EventState)anno;
                StateNode state = new StateNode();
                state.setName(eventStateAnno.stateName());
                state.setCode(eventStateAnno.stateCode());
                state.setStart(eventStateAnno.start());
                state.setEnd(eventStateAnno.end());
                state.setTimeout(eventStateAnno.timeout());
                state.setType(eventStateAnno.type());
                state.setClazzFullName(eventClass.getName());
                state.setClazzSimpleName(eventClass.getSimpleName());
                state.setEntityName(entityName);
                stateList.add(state);
            }
        }
        return stateList;
    }

    private static List<StateGroupNode> getStateGroupList(Class<?> eventClass, String entityName) {
        List<StateGroupNode> groupList = new ArrayList<>();
        Annotation[] annotations = eventClass.getAnnotations();
        for (Annotation anno : annotations) {
            if (anno instanceof EventStateGroup) {
                EventStateGroup eventStateGroupAnno = (EventStateGroup)anno;
                StateGroupNode group = new StateGroupNode();
                group.setName(eventStateGroupAnno.groupName());
                group.setCode(eventStateGroupAnno.groupCode());
                group.setStates(eventStateGroupAnno.states());
                group.setClazzFullName(eventClass.getName());
                group.setClazzSimpleName(eventClass.getSimpleName());
                group.setEntityName(entityName);
                groupList.add(group);
            }
        }
        return groupList;
    }

    private static List<EventNode> getEventList(Class<?> eventClass, Class<FsmEntity> entityClazz, Object bean, String beanName) {

        Method[] methods = eventClass.getMethods();

        List<EventNode> eventList = new ArrayList<>();
        for (Method method : methods) {
            RegisterEvent registerEventAnno = method.getAnnotation(RegisterEvent.class);
            if (registerEventAnno == null) {
                continue;
            }

            String clazzFullName = eventClass.getName();
            String methodName = method.getName();

            EventNode event = new EventNode();
            event.setCode(registerEventAnno.eventCode());
            event.setName(registerEventAnno.eventName());
            event.setSrcState(registerEventAnno.srcState());
            event.setDstState(registerEventAnno.dstState());

            event.setClazzFullName(clazzFullName);
            event.setClazzSimpleName(eventClass.getSimpleName());
            event.setMethodName(methodName);
            event.setBeanName(null);
            event.setEntityName(entityClazz.getSimpleName());
            event.setEntityClazz(entityClazz);
            event.setBeanName(beanName);
            event.setBean(bean);
            event.setMethod(method);

            // handle
            event.setBeforeHandleList(Arrays.asList(registerEventAnno.beforeHandles()));
            event.setHandleList(Arrays.asList(registerEventAnno.handles()));
            event.setAfterHandleList(Arrays.asList(registerEventAnno.afterHandles()));

            eventList.add(event);
        }
        return eventList;
    }
}
